// 版权所有2009 Go作者。版权所有。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

// HTTP请求读取和解析。

package http

import (
	"bufio"
	"bytes"
	"context"
	"crypto/tls"
	"encoding/base64"
	"errors"
	"fmt"
	"io"
	"mime"
	"mime/multipart"
	"net"
	"net/http/httptrace"
	"net/http/internal/ascii"
	"net/textproto"
	"net/url"
	urlpkg "net/url"
	"strconv"
	"strings"
	"sync"

	"golang.org/x/net/idna"
)

const (
	defaultMaxMemory = 32 << 20 // 32 MB 
)

// 当提供的文件字段名
// 在请求中不存在或不是文件字段时，FormFile将返回ErrMissingFile。
var ErrMissingFile = errors.New("http: no such file")

// 协议错误表示HTTP协议错误。
// 
// 已弃用：并非http包中与协议错误相关的所有错误
// 都属于ProtocolError类型。
type ProtocolError struct {
	ErrorString string
}

func (pe *ProtocolError) Error() string { return pe.ErrorString }

var (
	// ErrNotSupported由Pusher的Push方法返回，表示HTTP/2推送支持不可用。
	ErrNotSupported = &ProtocolError{"feature not supported"}

	// 已弃用：ErrUnexpectedTrailer不再由
	// 返回net/http包中的任何内容。调用方不应
	// 将错误与此变量进行比较。
	ErrUnexpectedTrailer = &ProtocolError{"trailer header without chunked transfer encoding"}

	// 当
	// 请求的内容类型不包含“boundary”参数时，Request.MultipartReader将返回ErrMissingBoundary。
	ErrMissingBoundary = &ProtocolError{"no multipart boundary param in Content-Type"}

	// 当
	// 请求的内容类型不是多部分/表单数据时，Request.MultipartReader返回ErrNotMultipart。
	ErrNotMultipart = &ProtocolError{"request Content-Type isn't multipart/form-data"}

	// 已弃用：ErrHeaderTooLong不再由
	// 返回net/http包中的任何内容。调用方不应
	// 将错误与此变量进行比较。
	ErrHeaderTooLong = &ProtocolError{"header too long"}

	// 已弃用：ErrShortBody不再由
	// 返回net/http包中的任何内容。调用方不应
	// 将错误与此变量进行比较。
	ErrShortBody = &ProtocolError{"entity body too short"}

	// 已弃用：ErrMissingContentLength不再由
	// 返回net/http包中的任何内容。调用方不应
	// 将错误与此变量进行比较。
	ErrMissingContentLength = &ProtocolError{"missing ContentLength in HEAD response"}
)

func badStringError(what, val string) error { return fmt.Errorf("%s %q", what, val) }

// 请求的标头。Write会自行处理，应跳过。
var reqWriteExcludeHeader = map[string]bool{
	"Host":              true, // 无论如何不在头映射中
	"User-Agent":        true,
	"Content-Length":    true,
	"Transfer-Encoding": true,
	"Trailer":           true,
}

// 请求表示由服务器接收的HTTP请求
// 或由客户端发送的HTTP请求。
// 
// 客户端和服务器之间的字段语义略有不同
// 用法。除了下面字段的注释外，请参见
// Request.Write和RoundTripper文档。
type Request struct {
	// 方法指定HTTP方法（GET、POST、PUT等）。
	// 对于客户端请求，空字符串表示GET。
	// 
	// Go的HTTP客户端不支持使用CONNECT方法发送请求。
	// 详见运输文件。
	Method string

	// URL指定被请求的URI（对于服务器
	// 请求）或要访问的URL（对于客户端请求）。
	// 
	// 对于服务器请求，URL是从请求行上提供的URI 
	// 解析而来的，该URI存储在RequestURI中。对于
	// 大多数请求，Path和RawQuery以外的字段将为
	// 空。（参见RFC 7230，第5.3节）
	// 
	// 对于客户端请求，URL的主机指定要连接的服务器
	// 而请求的主机字段可选
	// 指定要在HTTP 
	// 请求中发送的主机头值。
	URL *url.URL

	// 传入服务器请求的协议版本。
	// 
	// 对于客户端请求，这些字段将被忽略。HTTP 
	// 客户端代码始终使用HTTP/1.1或HTTP/2。
	// 详见运输单据。
	Proto      string // “HTTP/1.0”
	ProtoMajor int    // 1 
	ProtoMinor int    // 0 

	// 标头包含服务器收到的
	// 或客户端发送的请求标头字段。
	// 
	// 如果服务器收到带有标题行的请求，
	// 
	// 主机：example.com 
	// 接受编码：gzip，deflate 
	// 接受语言：en-us 
	// fOO:Bar 
	// fOO:two 
	// 
	// 然后
	// 
	// Header=map[string][]string{
	// /“接受编码”：{“gzip，deflate”}，
	// “Foo”：{“Bar”，“two”}，
	// }
	// /
	// 对于传入请求，主机头将升级为
	// /Request.Host字段，并从头映射中删除。
	// 
	// HTTP定义头名称不区分大小写。
	// 请求解析器通过使用CanonicalHeaderKey、
	// 将第一个字符和
	// 后面的任何字符设置为连字符大写，其余为小写来实现这一点。
	// 
	// 对于客户端请求，某些标头（如内容长度
	// 和连接）会在需要时自动写入，并且可以忽略标头中的值。请参阅文档
	// 了解Request.Write方法。
	Header Header

	// Body是请求的主体。
	// 
	// 对于客户端请求，零主体表示请求没有
	// 主体，例如GET请求。HTTP客户端的传输
	// 负责调用Close方法。
	// 
	// 对于服务器请求，请求正文始终为非零
	// 但在没有正文时将立即返回EOF。
	// 服务器将关闭请求正文。ServeHTTP 
	// 处理程序不需要。
	// 
	// 正文必须允许Read与Close同时调用。
	// 特别是，调用Close应该解除对等待输入的读取的阻塞。
	Body io.ReadCloser

	// GetBody定义了一个可选函数，用于返回
	// Body的新副本。当重定向需要多次读取正文时，它用于客户端请求。使用GetBody仍然
	// 需要设置Body。
	// 
	// 对于服务器请求，它是未使用的。
	GetBody func() (io.ReadCloser, error)

	// ContentLength记录关联内容的长度。
	// 值-1表示长度未知。
	// 值>=0表示可以从正文中读取给定数量的字节。
	// 
	// 对于客户端请求，具有非零正文的值0也被视为未知值。
	ContentLength int64

	// TransferEncoding列出从最外层到最内层的传输编码。空列表表示“标识”编码。
	// 通常可以忽略传输编码；分块编码是
	// 在发送和接收请求时根据需要自动添加和删除。
	TransferEncoding []string

	// 关闭指示是在
	// 响应此请求（对于服务器）后还是在发送此
	// 请求并读取其响应（对于客户端）后关闭连接。
	// 
	// 对于服务器请求，HTTP服务器将自动处理此字段
	// 处理程序不需要此字段。
	// 
	// 对于客户端请求，设置此字段可防止在到相同主机的请求之间重复使用
	// TCP连接，就像设置了
	// Transport.DisableKeepAlives一样。
	Close bool

	// 对于服务器请求，主机指定查找
	// URL的主机。对于HTTP/1（根据RFC 7230，第5.4节），此
	// 是URL本身中给定的“主机”头或主机名
	// 的值。对于HTTP/2，它是
	// /“：authority”伪头字段的值。
	// 它可以是“主机：端口”的形式。对于国际域名
	// 名称，主机可以是Punycode或Unicode格式。如果需要，请使用
	// golang.org/x/net/idna将其转换为任意一种格式。
	// 为防止DNS重新绑定攻击，服务器处理程序应
	// 验证主机头是否具有
	// 处理程序认为自己具有权威性的值。附带的
	// ServeMux支持注册到特定主机
	// 名称的模式，从而保护其注册的处理程序。
	// 
	// 对于客户端请求，主机可以选择性地覆盖要发送的主机
	// 头。如果为空，则Request.Write方法使用
	// URL.Host的值。主机可能包含国际
	// 域名。
	Host string

	// 表单包含解析后的表单数据，包括URL 
	// 字段的查询参数和补丁、POST或PUT表单数据。
	// 此字段仅在调用ParseForm后可用。
	// HTTP客户端忽略表单而使用Body。
	Form url.Values

	// PostForm包含来自补丁、POST 
	// 或PUT body参数的已解析表单数据。
	// 
	// 此字段仅在调用ParseForm后可用。
	// HTTP客户端忽略PostForm而使用Body。
	PostForm url.Values

	// MultipartForm是经过解析的多部分表单，包括文件上载。
	// 此字段仅在调用ParseMultipartForm后可用。
	// HTTP客户端忽略MultipartForm，而使用Body。
	MultipartForm *multipart.Form

	// 尾部指定在请求
	// 正文之后发送的附加头。
	// 
	// 对于服务器请求，尾部映射最初仅包含
	// 尾部密钥，值为零。（客户端声明它稍后将发送哪些拖车
	// 当处理程序从Body读取时，它必须
	// 不引用拖车。从Body读取返回EOF后，可以再次读取尾部
	// 并且如果客户端发送了
	// 则将包含非零值。
	// 
	// 对于客户端请求，必须将尾部初始化为包含
	// 稍后发送的尾部密钥的映射。这些值可以是零或它们的最终
	// 值。ContentLength必须为0或-1才能发送分块请求。
	// 发送HTTP请求后，可以在读取请求正文的同时更新映射值。一旦body返回EOF，调用方必须
	// not mutate trail。
	// 
	// 很少有HTTP客户端、服务器或代理支持HTTP拖车。
	Trailer Header

	// RemoteAddr允许HTTP服务器和其他软件记录发送请求的网络地址，通常用于
	// 日志记录。此字段不是由ReadRequest填写的，
	// 没有定义格式。此包中的HTTP服务器
	// 在调用
	// 处理程序之前，将RemoteAddr设置为“IP:port”地址。
	// 此字段被HTTP客户端忽略。
	RemoteAddr string

	// RequestURI是客户端
	// 请求行（RFC 7230，第3.1.1节）的未修改请求目标。通常应该使用URL字段。
	// 发送到服务器的
	// 在HTTP客户端请求中设置此字段是错误的。
	RequestURI string

	// TLS允许HTTP服务器和其他软件记录有关接收请求
	// 的TLS连接的信息。ReadRequest未填写此字段。
	// 此包中的HTTP服务器在调用处理程序之前为
	// 启用TLS的连接设置字段；
	// 否则将保留字段nil。
	// 此字段被HTTP客户端忽略。
	TLS *tls.ConnectionState

	// Cancel是一个可选通道，其关闭表示客户端
	// 请求应被视为已取消。并非所有
	// RoundTripper的实现都支持取消。
	// 
	// 对于服务器请求，此字段不适用。
	// 
	// 已弃用：改为使用NewRequestWithContext 
	// 设置请求的上下文。如果请求的Cancel字段和上下文都是
	// 集，则是否遵守Cancel是未定义的。
	Cancel <-chan struct{}

	// Response是导致创建此请求
	// 的重定向响应。此字段仅在客户端
	// /重定向期间填充。
	Response *Response

	// ctx是客户端或服务器上下文。只能通过使用WithContext复制整个请求来修改
	// 。
	// 防止人们错误地使用上下文
	// 并改变同一请求的调用方所持有的上下文，这是未报告的。
	ctx context.Context
}

// Context返回请求的上下文。要更改上下文，请使用
// WithContext。
// 
// 返回的上下文始终为非零；默认为
// 背景上下文。
// 
// 对于传出的客户端请求，上下文控制取消。
// 
// 对于传入的服务器请求，当
// 客户端连接关闭、请求被取消（使用HTTP/2）、
// 或ServeHTTP方法返回时，上下文被取消。
func (r *Request) Context() context.Context {
	if r.ctx != nil {
		return r.ctx
	}
	return context.Background()
}

// WithContext返回r的浅层副本，其上下文已更改为ctx。提供的ctx必须为非零。
// 
// 对于传出的客户端请求，上下文控制整个
// 请求及其响应的生存期：获取连接，
// 发送请求，读取响应头和响应体。
// 
// 要使用上下文创建新请求，请使用NewRequestWithContext。
// 若要更改请求的上下文，例如发送回前要修改的传入请求，请使用request.Clone。在
// 这两种用途之间，很少需要上下文。
func (r *Request) WithContext(ctx context.Context) *Request {
	if ctx == nil {
		panic("nil context")
	}
	r2 := new(Request)
	*r2 = *r
	r2.ctx = ctx
	r2.URL = cloneURL(r.URL) // 遗留行为；TODO:尝试删除。问题23544 
	return r2
}

// 克隆返回r的深度副本，其上下文更改为ctx。
// 提供的ctx必须为非零。
// 
// 对于传出的客户端请求，上下文控制请求及其响应的整个
// 生存期：获取连接，
// 发送请求，并读取响应头和响应体。
func (r *Request) Clone(ctx context.Context) *Request {
	if ctx == nil {
		panic("nil context")
	}
	r2 := new(Request)
	*r2 = *r
	r2.ctx = ctx
	r2.URL = cloneURL(r.URL)
	if r.Header != nil {
		r2.Header = r.Header.Clone()
	}
	if r.Trailer != nil {
		r2.Trailer = r.Trailer.Clone()
	}
	if s := r.TransferEncoding; s != nil {
		s2 := make([]string, len(s))
		copy(s2, s)
		r2.TransferEncoding = s2
	}
	r2.Form = cloneURLValues(r.Form)
	r2.PostForm = cloneURLValues(r.PostForm)
	r2.MultipartForm = cloneMultipartForm(r.MultipartForm)
	return r2
}

// 协议至少报告请求中使用的HTTP协议是否至少为major.minor。
func (r *Request) ProtoAtLeast(major, minor int) bool {
	return r.ProtoMajor > major ||
		r.ProtoMajor == major && r.ProtoMinor >= minor
}

// 如果在请求中发送，UserAgent将返回客户端的用户代理。
func (r *Request) UserAgent() string {
	return r.Header.Get("User-Agent")
}

// Cookies解析并返回随请求发送的HTTP Cookies。
func (r *Request) Cookies() []*Cookie {
	return readCookies(r.Header, "")
}

// 当找不到Cookie时，请求的Cookie方法将返回ErrNoCookie。
var ErrNoCookie = errors.New("http: named cookie not present")

// Cookie返回请求中提供的命名Cookie，如果未找到，则返回
// ErrNoCookie。
// 如果多个cookie与给定名称匹配，则只返回一个cookie 
// 。
func (r *Request) Cookie(name string) (*Cookie, error) {
	for _, c := range readCookies(r.Header, name) {
		return c, nil
	}
	return nil, ErrNoCookie
}

// AddCookie将cookie添加到请求中。根据RFC 6265第5.4节，
// AddCookie未附加多个Cookie标头字段。
// 表示所有cookie（如果有）都写入同一行，
// 以分号分隔。
// AddCookie只清理c的名称和值，不清理请求中已存在的Cookie头。
func (r *Request) AddCookie(c *Cookie) {
	s := fmt.Sprintf("%s=%s", sanitizeCookieName(c.Name), sanitizeCookieValue(c.Value))
	if c := r.Header.Get("Cookie"); c != "" {
		r.Header.Set("Cookie", c+"; "+s)
	} else {
		r.Header.Set("Cookie", s)
	}
}

// Referer返回请求中发送的引用URL。
// 
// Referer拼写错误，与请求本身一样，这是HTTP最早出现的时候的错误。该值也可以从
// 头映射中获取，作为头[“Referer”]；将其作为一种方法提供的好处是，编译器可以诊断使用
// alternate（correct English）spelling req.referer（）的程序，但不能诊断使用Header[“referer”]的程序。
func (r *Request) Referer() string {
	return r.Header.Get("Referer")
}

// multipartByReader是一个哨兵值。
// 它在Request.MultipartForm中的存在表示请求的解析
// 正文已传递给MultipartReader，而不是ParseMultipartForm。
var multipartByReader = &multipart.Form{
	Value: make(map[string][]string),
	File:  make(map[string][]*multipart.FileHeader),
}

// 如果这是一个多部分/表单数据或多部分/混合POST请求，则MultipartReader返回一个MIME多部分读取器，否则返回nil和一个错误。
// 使用此函数而不是ParseMultipartForm来
// 将请求正文作为流进行处理。
func (r *Request) MultipartReader() (*multipart.Reader, error) {
	if r.MultipartForm == multipartByReader {
		return nil, errors.New("http: MultipartReader called twice")
	}
	if r.MultipartForm != nil {
		return nil, errors.New("http: multipart handled by ParseMultipartForm")
	}
	r.MultipartForm = multipartByReader
	return r.multipartReader(true)
}

func (r *Request) multipartReader(allowMixed bool) (*multipart.Reader, error) {
	v := r.Header.Get("Content-Type")
	if v == "" {
		return nil, ErrNotMultipart
	}
	d, params, err := mime.ParseMediaType(v)
	if err != nil || !(d == "multipart/form-data" || allowMixed && d == "multipart/mixed") {
		return nil, ErrNotMultipart
	}
	boundary, ok := params["boundary"]
	if !ok {
		return nil, ErrMissingBoundary
	}
	return multipart.NewReader(r.Body, boundary), nil
}

// isH2Upgrade报告r是否代表http2“客户机前言”
// 魔术字符串。
func (r *Request) isH2Upgrade() bool {
	return r.Method == "PRI" && len(r.Header) == 0 && r.URL.Path == "*" && r.Proto == "HTTP/2.0"
}

// 如果非空则返回值，否则定义。
func valueOrDefault(value, def string) string {
	if value != "" {
		return value
	}
	return def
}

// 注意：这不是为了反映实际使用的Go版本。
// 在Go 1.1发布时已更改，因为前用户代理
// 已被某些入侵检测系统阻止。
// 参见https:
const defaultUserAgent = "Go-http-client/1.1"

// Write以wire格式写入HTTP/1.1请求，该请求是头和正文。
// 此方法引用请求的以下字段：
// 主机
// URL 
// 方法（默认为“GET”）
// Header 
// ContentLength 
// TransferEncoding 
// Body 
// 
// 如果Body存在，则内容长度<=0，且TransferEncoding 
// 尚未设置为“标识”，写入并添加“Transfer Encoding:
// chunked”到页眉。正文在发送后关闭。
func (r *Request) Write(w io.Writer) error {
	return r.write(w, false, nil, nil)
}

// WriteProxy与Write类似，但以HTTP代理所期望的形式写入请求。特别是，WriteProxy根据RFC 7230的
// 初始请求URI行。
// 第5.3节（包括方案和主机）使用绝对URI写入请求的
// 在这两种情况下，WriteProxy也会使用
// r.Host或r.URL.Host写入主机头。
func (r *Request) WriteProxy(w io.Writer) error {
	return r.write(w, true, nil, nil)
}

// 当请求中没有主机或URL时，将通过写入返回errMissingHost。
var errMissingHost = errors.New("http: Request.Write on Request with no Host or URL set")

// 外部标头可能为零
// waitForContinue可能为零
// 始终关闭正文
func (r *Request) write(w io.Writer, usingProxy bool, extraHeaders Header, waitForContinue func() bool) (err error) {
	trace := httptrace.ContextClientTrace(r.Context())
	if trace != nil && trace.WroteRequest != nil {
		defer func() {
			trace.WroteRequest(httptrace.WroteRequestInfo{
				Err: err,
			})
		}()
	}
	closed := false
	defer func() {
		if closed {
			return
		}
		if closeErr := r.closeBody(); closeErr != nil && err == nil {
			err = closeErr
		}
	}()

	// 查找目标主机。首选Host:header，但如果未提供该
	// ，请使用请求URL中的主机。
	// 
	// 清理主机，以防主机中含有意外内容。
	host := cleanHost(r.Host)
	if host == "" {
		if r.URL == nil {
			return errMissingHost
		}
		host = cleanHost(r.URL.Host)
	}

	// 根据RFC 6874，HTTP客户端、代理或其他
	// 中介必须删除附加到传出URI的任何IPv6区域标识符。
	host = removeZone(host)

	ruri := r.URL.RequestURI()
	if usingProxy && r.URL.Scheme != "" && r.URL.Opaque == "" {
		ruri = r.URL.Scheme + ":// “+host+ruri 
	} else if r.Method == "CONNECT" && r.URL.Path == "" {
		// 连接请求通常只提供主机和端口，而不是完整的URL。
		ruri = host
		if r.URL.Opaque != "" {
			ruri = r.URL.Opaque
		}
	}
	if stringContainsCTLByte(ruri) {
		return errors.New("net/http: can't write control character in Request.URL")
	}
	// TODO:也验证r.方法？至少
	// 不太可能来自攻击者（更可能是
	// 代码中的常量）.

	// 如果尚未缓冲，则将写入程序包装在bufio写入程序中。
	// 不要总是调用NewWriter，因为这会强制使用字节。Buffer 
	// 和其他小型bufio写入程序至少有4k缓冲区
	// 大小。
	var bw *bufio.Writer
	if _, ok := w.(io.ByteWriter); !ok {
		bw = bufio.NewWriter(w)
		w = bw
	}

	_, err = fmt.Fprintf(w, "%s %s HTTP/1.1\r\n", valueOrDefault(r.Method, "GET"), ruri)
	if err != nil {
		return err
	}

	// 头行
	_, err = fmt.Fprintf(w, "Host: %s\r\n", host)
	if err != nil {
		return err
	}
	if trace != nil && trace.WroteHeaderField != nil {
		trace.WroteHeaderField("Host", []string{host})
	}

	// 使用默认值UserAgent，除非标头包含一个，否则
	// 可能为空以不发送标头。
	userAgent := defaultUserAgent
	if r.Header.has("User-Agent") {
		userAgent = r.Header.Get("User-Agent")
	}
	if userAgent != "" {
		_, err = fmt.Fprintf(w, "User-Agent: %s\r\n", userAgent)
		if err != nil {
			return err
		}
		if trace != nil && trace.WroteHeaderField != nil {
			trace.WroteHeaderField("User-Agent", []string{userAgent})
		}
	}

	// 进程正文，ContentLength，Close，Trailer 
	tw, err := newTransferWriter(r)
	if err != nil {
		return err
	}
	err = tw.writeHeader(w, trace)
	if err != nil {
		return err
	}

	err = r.Header.writeSubset(w, reqWriteExcludeHeader, trace)
	if err != nil {
		return err
	}

	if extraHeaders != nil {
		err = extraHeaders.write(w, trace)
		if err != nil {
			return err
		}
	}

	_, err = io.WriteString(w, "\r\n")
	if err != nil {
		return err
	}

	if trace != nil && trace.WroteHeaders != nil {
		trace.WroteHeaders()
	}

	// 刷新并等待100如果需要继续。
	if waitForContinue != nil {
		if bw, ok := w.(*bufio.Writer); ok {
			err = bw.Flush()
			if err != nil {
				return err
			}
		}
		if trace != nil && trace.Wait100Continue != nil {
			trace.Wait100Continue()
		}
		if !waitForContinue() {
			closed = true
			r.closeBody()
			return nil
		}
	}

	if bw, ok := w.(*bufio.Writer); ok && tw.FlushHeaders {
		if err := bw.Flush(); err != nil {
			return err
		}
	}

	// 写入正文和尾部
	closed = true
	err = tw.writeBody(w)
	if err != nil {
		if tw.bodyReadError == err {
			err = requestBodyReadError{err}
		}
		return err
	}

	if bw != nil {
		return bw.Flush()
	}
	return nil
}

// requestBodyReadError从（*请求）写入指示
type requestBodyReadError struct{ error }

func idnaASCII(v string) (string, error) {
	// 允许的字符测试都被省略。它还可以防止ToASCII 
	// 调用在可能的情况下抢救无效的IDN。因此，
	// 可能有两个IDN，它们与
	// 仅使用ASCII版本的用户相同这是一个下游错误，而非ASCII 
	// 版本没有。
	// 请注意，对于正确的ASCII ID，ASCII只会做更多的
	// 工作，但不会导致分配。
	if ascii.Is(v) {
		return v, nil
	}
	return idna.Lookup.ToASCII(v)
}

// 清理主机清理在请求的主机头中发送的主机。
// 
// 这两个函数都会删除“/”或“”之后的任何内容，并在必要时将值
// 转换为Punycode形式。
// 
// 理想情况下，我们会根据规范清理主机头：
// https:
// https:
// https:
// 但实际上，我们试图避免的是在
// 问题11206中的情况，在代理上下文中使用格式错误的主机头
// 将创建一个错误的请求。因此，仅截断
// 第一个违规字符就足够了。
func cleanHost(in string) string {
	if i := strings.IndexAny(in, " /"); i != -1 {
		in = in[:i]
	}
	host, port, err := net.SplitHostPort(in)
	if err != nil { // 输入只是一个错误主机
		a, err := idnaASCII(in)
		if err != nil {
			return in // 垃圾输入，垃圾输出
		}
		return a
	}
	a, err := idnaASCII(host)
	if err != nil {
		return in // 垃圾输入，垃圾输出
	}
	return net.JoinHostPort(a, port)
}

// removeZone从主机中删除IPv6区域标识符。
// 例如，“[fe80:：1%en0]：8080”到“[fe80:：1]：8080”
func removeZone(host string) string {
	if !strings.HasPrefix(host, "[") {
		return host
	}
	i := strings.LastIndex(host, "]")
	if i < 0 {
		return host
	}
	j := strings.LastIndex(host[:i], "%")
	if j < 0 {
		return host
	}
	return host[:j] + host[i:]
}

// ParseHTTPVersion解析HTTP版本字符串。
// /“HTTP/1.0”返回（1，0，true）。请注意，不带
// 是次要版本，例如“HTTP/2”，无效。
func ParseHTTPVersion(vers string) (major, minor int, ok bool) {
	const Big = 1000000 // 任意上限
	switch vers {
	case "HTTP/1.1":
		return 1, 1, true
	case "HTTP/1.0":
		return 1, 0, true
	}
	if !strings.HasPrefix(vers, "HTTP/") {
		return 0, 0, false
	}
	dot := strings.Index(vers, ".")
	if dot < 0 {
		return 0, 0, false
	}
	major, err := strconv.Atoi(vers[5:dot])
	if err != nil || major < 0 || major > Big {
		return 0, 0, false
	}
	minor, err = strconv.Atoi(vers[dot+1:])
	if err != nil || minor < 0 || minor > Big {
		return 0, 0, false
	}
	return major, minor, true
}

func validMethod(method string) bool {
	/*
	     Method         = "OPTIONS"                ; Section 9.2
	                    | "GET"                    ; Section 9.3
	                    | "HEAD"                   ; Section 9.4
	                    | "POST"                   ; Section 9.5
	                    | "PUT"                    ; Section 9.6
	                    | "DELETE"                 ; Section 9.7
	                    | "TRACE"                  ; Section 9.8
	                    | "CONNECT"                ; Section 9.9
	                    | extension-method
	   extension-method = token
	     token          = 1*<any CHAR except CTLs or separators>
	*/
	return len(method) > 0 && strings.IndexFunc(method, isNotToken) == -1
}

// NewRequest使用context.Background.
func NewRequest(method, url string, body io.Reader) (*Request, error) {
	return NewRequestWithContext(context.Background(), method, url, body)
}

// NewRequestWithContext返回一个新请求，给定方法、URL和
// 可选正文。
// 
// 如果提供的正文也是io.Clos呃，返回的
// Request.Body设置为Body，将由客户端
// 方法Do、Post、PostForm和Transport.RoundTrip.
// 
// NewRequestWithContext返回适合与
// Client.Do或Transport.RoundTrip一起使用的请求，以创建请求t如果要与
// 测试服务器处理程序一起使用，请使用
// net/http/httptest包中的NewRequest函数，使用ReadRequest，或手动更新
// 请求字段。对于传出的客户端请求，上下文
// 控制请求及其响应的整个生存期：
// 获取连接、发送请求并读取
// 响应头和正文。有关
// 入站和出站请求字段之间的差异。
// 
// 如果正文类型为*bytes.Buffer、*bytes.Reader或
// /*strings.Reader，则返回的请求的ContentLength设置为其
// 精确值（而不是-1），填充GetBody（因此307和308 
// 重定向可以重播正文），如果
// ContentLength为0，则Body设置为NoBody。
func NewRequestWithContext(ctx context.Context, method, url string, body io.Reader) (*Request, error) {
	if method == "" {
		// 我们文档中的“”表示Request.Method的“GET”，而人们已经使用了
		// 依赖于NewRequest中的方法，所以请继续使用它。
		// 我们仍然对非空方法强制使用validMethod。
		method = "GET"
	}
	if !validMethod(method) {
		return nil, fmt.Errorf("net/http: invalid method %q", method)
	}
	if ctx == nil {
		return nil, errors.New("net/http: nil Context")
	}
	u, err := urlpkg.Parse(url)
	if err != nil {
		return nil, err
	}
	rc, ok := body.(io.ReadCloser)
	if !ok && body != nil {
		rc = io.NopCloser(body)
	}
	// 主机的冒号：端口应规范化。见第14836期。
	u.Host = removeEmptyPort(u.Host)
	req := &Request{
		ctx:        ctx,
		Method:     method,
		URL:        u,
		Proto:      "HTTP/1.1",
		ProtoMajor: 1,
		ProtoMinor: 1,
		Header:     make(Header),
		Body:       rc,
		Host:       u.Host,
	}
	if body != nil {
		switch v := body.(type) {
		case *bytes.Buffer:
			req.ContentLength = int64(v.Len())
			buf := v.Bytes()
			req.GetBody = func() (io.ReadCloser, error) {
				r := bytes.NewReader(buf)
				return io.NopCloser(r), nil
			}
		case *bytes.Reader:
			req.ContentLength = int64(v.Len())
			snapshot := *v
			req.GetBody = func() (io.ReadCloser, error) {
				r := snapshot
				return io.NopCloser(&r), nil
			}
		case *strings.Reader:
			req.ContentLength = int64(v.Len())
			snapshot := *v
			req.GetBody = func() (io.ReadCloser, error) {
				r := snapshot
				return io.NopCloser(&r), nil
			}
		default:
			// 我们在这里将其设置为-1（至少
			// 如果body！=NoBody！=NoBody），表示未知，但
			// 在Go 1.8测试
			// 期间使人崩溃。人们相信它是0，我猜。也许稍后再试。见第18117期。
		}
		// 对于客户端请求，Request.ContentLength为0 
		// 表示实际为0或未知。
		// 明确表示ContentLength为零的唯一方法是将Body设置为nil。但是太多的代码
		// 依赖于返回非零正文的NewRequest，
		// 所以我们使用了一个著名的ReadCloser变量来代替
		// 并且让http包也将sentinel 
		// 变量显式地表示为零。如果请求使用HTTP基本身份验证，则返回请求的
		if req.GetBody != nil && req.ContentLength == 0 {
			req.Body = NoBody
			req.GetBody = func() (io.ReadCloser, error) { return NoBody, nil }
		}
	}

	return req, nil
}

// 授权头中提供的用户名和密码。
// 参见RFC 2617第2节。
func (r *Request) BasicAuth() (username, password string, ok bool) {
	auth := r.Header.Get("Authorization")
	if auth == "" {
		return
	}
	return parseBasicAuth(auth)
}

// parseBasicAuth解析HTTP基本身份验证字符串。
// /“基本QWXHZGRPBIVCGVUIHNLC2FTZQ==”返回（“阿拉丁”，“芝麻开门”，真）。
func parseBasicAuth(auth string) (username, password string, ok bool) {
	const prefix = "Basic "
	// 不区分大小写的前缀匹配。见第22736期。
	if len(auth) < len(prefix) || !ascii.EqualFold(auth[:len(prefix)], prefix) {
		return
	}
	c, err := base64.StdEncoding.DecodeString(auth[len(prefix):])
	if err != nil {
		return
	}
	cs := string(c)
	s := strings.IndexByte(cs, ':')
	if s < 0 {
		return
	}
	return cs[:s], cs[s+1:], true
}

// SetBasicAuth将请求的授权头设置为使用HTTP 
// 使用提供的用户名和密码进行基本身份验证。
// 
// 通过HTTP基本身份验证，提供的用户名和密码
// 未加密。
// 
// 某些协议可能会对预先转义
// 用户名和密码提出额外要求。例如，当与OAuth2一起使用时，两个参数
// 必须首先使用URL.queryscape进行URL编码。
func (r *Request) SetBasicAuth(username, password string) {
	r.Header.Set("Authorization", "Basic "+basicAuth(username, password))
}

// parseRequestLine将“GET/foo HTTP/1.1”解析为三个部分。
func parseRequestLine(line string) (method, requestURI, proto string, ok bool) {
	s1 := strings.Index(line, " ")
	s2 := strings.Index(line[s1+1:], " ")
	if s1 < 0 || s2 < 0 {
		return
	}
	s2 += s1 + 1
	return line[:s1], line[s1+1 : s2], line[s2+1:], true
}

var textprotoReaderPool sync.Pool

func newTextprotoReader(br *bufio.Reader) *textproto.Reader {
	if v := textprotoReaderPool.Get(); v != nil {
		tr := v.(*textproto.Reader)
		tr.R = br
		return tr
	}
	return textproto.NewReader(br)
}

func putTextprotoReader(r *textproto.Reader) {
	r.R = nil
	textprotoReaderPool.Put(r)
}

// ReadRequest读取并解析来自b的传入请求。
// 
// ReadRequest是一个低级函数，只能用于
// 专用应用；大多数代码应该使用服务器读取
// 请求，并通过处理程序接口处理它们。ReadRequest 
// 仅支持HTTP/1.x请求。对于HTTP/2，请使用golang.org/x/net/http2。
func ReadRequest(b *bufio.Reader) (*Request, error) {
	req, err := readRequest(b)
	if err != nil {
		return nil, err
	}

	delete(req.Header, "Host")
	return req, err
}

func readRequest(b *bufio.Reader) (req *Request, err error) {
	tp := newTextprotoReader(b)
	req = new(Request)

	// 第一行：GET/index.html HTTP/1.0 
	var s string
	if s, err = tp.ReadLine(); err != nil {
		return nil, err
	}
	defer func() {
		putTextprotoReader(tp)
		if err == io.EOF {
			err = io.ErrUnexpectedEOF
		}
	}()

	var ok bool
	req.Method, req.RequestURI, req.Proto, ok = parseRequestLine(s)
	if !ok {
		return nil, badStringError("malformed HTTP request", s)
	}
	if !validMethod(req.Method) {
		return nil, badStringError("invalid method", req.Method)
	}
	rawurl := req.RequestURI
	if req.ProtoMajor, req.ProtoMinor, ok = ParseHTTPVersion(req.Proto); !ok {
		return nil, badStringError("malformed HTTP version", req.Proto)
	}

	// 连接请求有两种不同的使用方式，都不使用完整的URL:
	// 标准用法是通过HTTP代理对HTTPS进行隧道传输。
	// 看起来像“CONNECT www.google.com:443 HTTP/1.1”，参数是
	// 只是URL的授权部分。此信息应放在req.URL.Host中。
	// 
	// net/rpc包也使用CONNECT，但其中的参数是以斜杠开头的路径
	// 。可以使用常规URL解析器
	// 对其进行解析，该路径最终将位于req.URL.path中，该路径必须位于该路径中才能使
	// RPC正常工作。
	justAuthority := req.Method == "CONNECT" && !strings.HasPrefix(rawurl, "/")
	if justAuthority {
		rawurl = "http:// “+rawurl 
	}

	if req.URL, err = url.ParseRequestURI(rawurl); err != nil {
		return nil, err
	}

	if justAuthority {
		// 去除伪“http:
		req.URL.Scheme = ""
	}

	// 后续行：Key:value。
	mimeHeader, err := tp.ReadMIMEHeader()
	if err != nil {
		return nil, err
	}
	req.Header = Header(mimeHeader)
	if len(req.Header["Host"]) > 1 {
		return nil, fmt.Errorf("too many Host headers")
	}

	// RFC 7230，第5.3节：必须治疗
	// GET/index.html HTTP/1.1 
	// 主机：www.google.com 
	// 和
	// 获取HTTP:
	// 主机：DoesMatter 
	// 相同。在第二种情况下，将忽略任何主体行。
	req.Host = req.URL.Host
	if req.Host == "" {
		req.Host = req.Header.get("Host")
	}

	fixPragmaCacheControl(req.Header)

	req.Close = shouldClose(req.ProtoMajor, req.ProtoMinor, req.Header, false)

	err = readTransfer(req, b)
	if err != nil {
		return nil, err
	}

	if req.isH2Upgrade() {
		// 因为它既没有分块，也没有声明：
		req.ContentLength = -1

		// 我们想给处理程序一个机会劫持
		// 连接，但我们需要阻止服务器进一步处理连接，如果它没有被劫持。设置Close以确保：
		req.Close = true
	}
	return req, nil
}

// MaxBytesReader与io.LimitReader类似，但用于限制传入请求主体的大小。与
// io.LimitReader，MaxByteReader的结果是ReadCloser，对于超出限制的读取返回
// 非EOF错误，并在调用其Close方法时关闭
// 基础读取器。
// 
// MaxByteReader可防止客户端意外或恶意发送大型请求并浪费服务器资源。
func MaxBytesReader(w ResponseWriter, r io.ReadCloser, n int64) io.ReadCloser {
	if n < 0 { // 将负极限视为等于0。
		n = 0
	}
	return &maxBytesReader{w: w, r: r, n: n}
}

type maxBytesReader struct {
	w   ResponseWriter
	r   io.ReadCloser // 底层读卡器
	n   int64         // 剩余最大字节数
	err error         // 粘性错误
}

func (l *maxBytesReader) Read(p []byte) (n int, err error) {
	if l.err != nil {
		return 0, l.err
	}
	if len(p) == 0 {
		return 0, nil
	}
	// 如果他们要求读取32KB字节，但只剩下5个字节，则无需读取32KB。6字节将回答
	// 关于我们是否达到极限或超过极限的问题。
	if int64(len(p)) > l.n+1 {
		p = p[:l.n+1]
	}
	n, err = l.r.Read(p)

	if int64(n) <= l.n {
		l.n -= int64(n)
		l.err = err
		return n, err
	}

	n = int(l.n)
	l.n = 0

	// 服务器代码和客户端代码都使用
	// MaxByteReader。此“requestTooLarge”检查仅由服务器代码使用。为了防止仅使用HTTP客户端代码（如
	// 也在HTTP服务器中链接，请不要对服务器
	// cmd/go）的二进制文件
	// “*响应”类型使用静态类型断言。改为检查此接口：
	type requestTooLarger interface {
		requestTooLarge()
	}
	if res, ok := l.w.(requestTooLarger); ok {
		res.requestTooLarge()
	}
	l.err = errors.New("http: request body too large")
	return n, l.err
}

func (l *maxBytesReader) Close() error {
	return l.r.Close()
}

func copyValues(dst, src url.Values) {
	for k, vs := range src {
		dst[k] = append(dst[k], vs...)
	}
}

func parsePostForm(r *Request) (vs url.Values, err error) {
	if r.Body == nil {
		err = errors.New("missing form body")
		return
	}
	ct := r.Header.Get("Content-Type")
	// RFC 7231，第3.1.1.5节-空类型
	// 可能被视为应用程序/八位字节流
	if ct == "" {
		ct = "application/octet-stream"
	}
	ct, _, err = mime.ParseMediaType(ct)
	switch {
	case ct == "application/x-www-form-urlencoded":
		var reader io.Reader = r.Body
		maxFormSize := int64(1<<63 - 1)
		if _, ok := r.Body.(*maxBytesReader); !ok {
			maxFormSize = int64(10 << 20) // 10 MB是大量文本。
			reader = io.LimitReader(r.Body, maxFormSize+1)
		}
		b, e := io.ReadAll(reader)
		if e != nil {
			if err == nil {
				err = e
			}
			break
		}
		if int64(len(b)) > maxFormSize {
			err = errors.New("http: POST too large")
			return
		}
		vs, e = url.ParseQuery(string(b))
		if err == nil {
			err = e
		}
	case ct == "multipart/form-data":
		// 由ParseMultipartForm（正在调用我们，或应该调用我们）处理
		// TODO（bradfitz）：可能的
		// 命令太多，无法在此处调用太多函数。
		// 清理此文件并编写更多测试。
		// request_test.go包含此文件的开头，
		// in TestParseMultipartFormOrder和其他文件。
	}
	return
}

// ParseForm填充r.Form和r.PostForm。
// 
// 对于所有请求，ParseForm从URL解析原始查询并更新
// r.Form。
// 
// 对于POST、PUT和PATCH请求，它还读取请求正文，将其解析为表单
// 并将结果同时放入r.PostForm和r.form中。请求正文
// 参数优先于r.Form中的URL查询字符串值。
// 
// 如果请求正文的大小尚未受到MaxByteReader的限制，则
// 大小上限为10MB。
// 
// 对于其他HTTP方法，或者当内容类型不是
// application/x-www-form-urlencoded时，不读取请求正文，并且
// r.PostForm被初始化为非零的空值。
// 
// ParseMultipartForm自动调用ParseForm。
// ParseForm是幂等的。
func (r *Request) ParseForm() error {
	var err error
	if r.PostForm == nil {
		if r.Method == "POST" || r.Method == "PUT" || r.Method == "PATCH" {
			r.PostForm, err = parsePostForm(r)
		}
		if r.PostForm == nil {
			r.PostForm = make(url.Values)
		}
	}
	if r.Form == nil {
		if len(r.PostForm) > 0 {
			r.Form = make(url.Values)
			copyValues(r.Form, r.PostForm)
		}
		var newValues url.Values
		if r.URL != nil {
			var e error
			newValues, e = url.ParseQuery(r.URL.RawQuery)
			if err == nil {
				err = e
			}
		}
		if newValues == nil {
			newValues = make(url.Values)
		}
		if r.Form == nil {
			r.Form = newValues
		} else {
			copyValues(r.Form, newValues)
		}
	}
	return err
}

// ParseMultipartForm将请求正文解析为多部分/表单数据。
// 解析整个请求体，
// 其文件部分存储在内存中，其余部分存储在临时文件中的
// 磁盘上。
// ParseMultipartForm在必要时调用ParseForm。
// 如果ParseForm返回错误，则ParseMultipartForm将返回错误，但
// 将继续解析请求正文。
// 调用ParseMultipartForm一次后，后续调用无效。
func (r *Request) ParseMultipartForm(maxMemory int64) error {
	if r.MultipartForm == multipartByReader {
		return errors.New("http: multipart handled by MultipartReader")
	}
	var parseFormErr error
	if r.Form == nil {
		// 让ParseForm中的错误消失，只需
		// 在最后返回它。
		parseFormErr = r.ParseForm()
	}
	if r.MultipartForm != nil {
		return nil
	}

	mr, err := r.multipartReader(false)
	if err != nil {
		return err
	}

	f, err := mr.ReadForm(maxMemory)
	if err != nil {
		return err
	}

	if r.PostForm == nil {
		r.PostForm = make(url.Values)
	}
	for k, v := range f.Value {
		r.Form[k] = append(r.Form[k], v...)
		// r.PostForm也应填充。见第9305期。
		r.PostForm[k] = append(r.PostForm[k], v...)
	}

	r.MultipartForm = f

	return parseFormErr
}

// FormValue返回查询的命名组件的第一个值。
// POST和PUT正文参数优先于URL查询字符串值。
// FormValue在必要时调用ParseMultipartForm和ParseForm，并忽略
// 这些函数返回的任何错误。
// 如果键不存在，FormValue将返回空字符串。
// 要访问同一个键的多个值，请调用ParseForm和
// 然后直接检查Request.Form。
func (r *Request) FormValue(key string) string {
	if r.Form == nil {
		r.ParseMultipartForm(defaultMaxMemory)
	}
	if vs := r.Form[key]; len(vs) > 0 {
		return vs[0]
	}
	return ""
}

// PostFormValue返回POST的命名组件的第一个值，即
// PATCH或PUT请求体。URL查询参数被忽略。
// PostFormValue在必要时调用ParseMultipartForm和ParseForm，并忽略
// 这些函数返回的任何错误。
// 如果键不存在，则PostFormValue返回空字符串。
func (r *Request) PostFormValue(key string) string {
	if r.PostForm == nil {
		r.ParseMultipartForm(defaultMaxMemory)
	}
	if vs := r.PostForm[key]; len(vs) > 0 {
		return vs[0]
	}
	return ""
}

// FormFile返回所提供表单键的第一个文件。
// FormFile在必要时调用ParseMultipartForm和ParseForm。
func (r *Request) FormFile(key string) (multipart.File, *multipart.FileHeader, error) {
	if r.MultipartForm == multipartByReader {
		return nil, nil, errors.New("http: multipart handled by MultipartReader")
	}
	if r.MultipartForm == nil {
		err := r.ParseMultipartForm(defaultMaxMemory)
		if err != nil {
			return nil, nil, err
		}
	}
	if r.MultipartForm != nil && r.MultipartForm.File != nil {
		if fhs := r.MultipartForm.File[key]; len(fhs) > 0 {
			f, err := fhs[0].Open()
			return f, fhs[0], err
		}
	}
	return nil, nil, ErrMissingFile
}

func (r *Request) expectsContinue() bool {
	return hasToken(r.Header.get("Expect"), "100-continue")
}

func (r *Request) wantsHttp10KeepAlive() bool {
	if r.ProtoMajor != 1 || r.ProtoMinor != 0 {
		return false
	}
	return hasToken(r.Header.get("Connection"), "keep-alive")
}

func (r *Request) wantsClose() bool {
	if r.Close {
		return true
	}
	return hasToken(r.Header.get("Connection"), "close")
}

func (r *Request) closeBody() error {
	if r.Body == nil {
		return nil
	}
	return r.Body.Close()
}

func (r *Request) isReplayable() bool {
	if r.Body == nil || r.Body == NoBody || r.GetBody != nil {
		switch valueOrDefault(r.Method, "GET") {
		case "GET", "HEAD", "OPTIONS", "TRACE":
			return true
		}
		// 幂等键虽然是非标准的，但被广泛用于
		// 表示POST或其他请求是幂等的。请参阅
		// https:
		if r.Header.has("Idempotency-Key") || r.Header.has("X-Idempotency-Key") {
			return true
		}
	}
	return false
}

// outgoingLength报告此传出（客户端）请求的内容长度。
// 当主体为非nil时，它将0映射到-1（未知）。
func (r *Request) outgoingLength() int64 {
	if r.Body == nil || r.Body == NoBody {
		return 0
	}
	if r.ContentLength != 0 {
		return r.ContentLength
	}
	return -1
}

// requestmethodusuallyacksbody报告给定的请求
// 方法是否通常不涉及请求体。
// 这是传输（通过
// transferWriter.shouldSendChunkedRequestBody）用来确定
// 当
// Request.outgoingLength（）返回-1时，我们是否尝试从非零请求.Body中读取一个字节。请参见
// shouldSendChunkedRequestBody中的注释。
func requestMethodUsuallyLacksBody(method string) bool {
	switch method {
	case "GET", "HEAD", "DELETE", "OPTIONS", "PROPFIND", "SEARCH":
		return true
	}
	return false
}

// requiresHTTP1报告此请求是否需要通过HTTP/1连接发送。
func (r *Request) requiresHTTP1() bool {
	return hasToken(r.Header.Get("Connection"), "upgrade") &&
		ascii.EqualFold(r.Header.Get("Upgrade"), "websocket")
}
